package graph;

import java.util.ArrayList;

import task_system.TaskSystem;
import exception.ImpossibleTaskConfigurationException;

public class Graph {

	public int m;

	ArrayList<Node> failure_nodes_scheduler = new ArrayList<Node>();
	ArrayList<Node> failure_nodes_system = new ArrayList<Node>();

	ArrayList<Node> visited = new ArrayList<Node>();

	private TaskSystem taskSystem;

	public Graph(TaskSystem ts) {
		taskSystem = ts;
	}

	public void calculate_bad_nodes(){
		try {
			int size = taskSystem.tasks().size();
			failureNodesSystem(0, size, new ArrayList<TaskConfiguration>());
			failureNodesScheduler(0, size, new ArrayList<TaskConfiguration>());
			System.out.println(failure_nodes_system.size() + " " + failure_nodes_scheduler.size());
			optimization();
			System.out.println(failure_nodes_system.size() + " " + failure_nodes_scheduler.size());
			System.out.println("done");
		} catch (ImpossibleTaskConfigurationException e) {
			System.out.println(e.message());
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	private void failureNodesScheduler(int number, int size,
			ArrayList<TaskConfiguration> nodeConfig)
			throws ImpossibleTaskConfigurationException {
		for (int p = 0; p <= taskSystem.tasks().get(number).P(); p++) {
				int d = p - (taskSystem.tasks().get(number).P() - taskSystem.tasks().get(number).D());
				if (d < 0 )
					d=0;
			for (int c = 0; c <= taskSystem.tasks().get(number).C(); c++) {

				ArrayList<TaskConfiguration> nc = (ArrayList<TaskConfiguration>) nodeConfig.clone();
				if(taskSystem.tasks().get(number).compatibleConfiguration(p, d, c)){

					nc.add(new TaskConfiguration_Scheduler(p, d, c,
							taskSystem.tasks().get(number)));
				}
				if (number == size - 1) {
					if (nc.size() == size) {
						Node_Scheduler n = new Node_Scheduler(nc);
						if (n.failure()) {
							failure_nodes_scheduler.add(n);
						}
					}
				} else {
					failureNodesScheduler(number + 1, size, nc);
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	private void failureNodesSystem(int number, int size,
			ArrayList<TaskConfiguration> nodeConfig)
			throws ImpossibleTaskConfigurationException {
		
		for (int p = 0; p <= taskSystem.tasks().get(number).P(); p++) {
			int d = p - (taskSystem.tasks().get(number).P() - taskSystem.tasks().get(number).D());
			if (d < 0 )
				d=0;
			for (int c = 0; c <= taskSystem.tasks().get(number).C(); c++) {

				ArrayList<TaskConfiguration> nc = (ArrayList<TaskConfiguration>) nodeConfig.clone();
				if(taskSystem.tasks().get(number).compatibleConfiguration(p, d, c) && taskSystem.tasks().get(number).compatibleTaskConfigurationSystem(p, d, c)){
					nc.add(new TaskConfiguration_System(p, d, c, taskSystem
							.tasks().get(number)));
				}
				if (number == size - 1) {
					if (nc.size() == size) {
						Node_System n = new Node_System(nc);
						if (n.failure()) {
							failure_nodes_system.add(n);
						}
					}
				} else {
					failureNodesSystem(number + 1, size, nc);
				}
			}
		}
	}

	public ArrayList<Node> failure_nodes_scheduler() {
		return failure_nodes_scheduler;
	}

	public ArrayList<Node> failure_nodes_system() {
		return failure_nodes_system;
	}
	
	private boolean is_in_failure_system(Node n){
		for(Node n_sys : failure_nodes_system){
			if (n_sys.equals(n)){
				return true;
			}
		}
		return false;
	}
	
	private boolean is_in_failure_scheduler(Node n){
		for(Node n_sch : failure_nodes_scheduler){
			if (n_sch.equals(n)){
				return true;
			}
		}
		return false;
	}
	
	public String toString(){
		String result = "";
				
		result += "\nSystem failure states \n\n";
		result += failure_nodes_system.size();
		for(Node n : failure_nodes_system){
			result += "System\n"+ n.toString() + "\n";
		}
		
		result += "Scheduler failure states\n\n";
		result += failure_nodes_scheduler.size();

		for(Node n : failure_nodes_scheduler){
			result += "Scheduler\n"+ n.toString() + "\n";
		}
		return result;
	}
	
	public Node_System initial_state(){
		ArrayList<TaskConfiguration> tcs = new ArrayList<TaskConfiguration>();

		for (int i=0; i < taskSystem.tasks().size(); i++){
			try {
				tcs.add(new TaskConfiguration_System(0,0,0,taskSystem.tasks().get(i)));

			} catch (ImpossibleTaskConfigurationException e) {
				e.printStackTrace();
			}
		}
		return new Node_System(tcs);
	}
	
	@SuppressWarnings("all")
	private void optimization(){
		ArrayList<Node> copy_system = (ArrayList<Node>)failure_nodes_system.clone();
		ArrayList<Node> copy_scheduler = (ArrayList<Node>)failure_nodes_scheduler.clone();

		for(Node fs : copy_system){
			for(Node prec : fs.predecessors()){
//				boolean all_predecessors_in_failure = true;
//				if(!is_in_failure_scheduler(prec))
//					all_predecessors_in_failure = false;
//				if(all_predecessors_in_failure)
//					failure_nodes_system.remove(fs);
				if(is_in_failure_scheduler(prec)){
					failure_nodes_system.remove(fs);
					break;
				}
			}
		}
		

		for(Node fs : copy_scheduler){
			for(Node prec : fs.predecessors()){
				if(is_in_failure_system(prec)){
						failure_nodes_scheduler.remove(fs);
						break;
				}
			}
		}
		
		
	}
}
